"""
统一菜单管理服务层
"""

import json
import logging
import os
import re
from datetime import datetime
from typing import Dict, List, Any, Optional
from django.db import transaction
from django.core.exceptions import ValidationError
from django.conf import settings

from cmdb.models import MenuItem as DynamicMenu

logger = logging.getLogger(__name__)


class MenuConsistencyAnalyzer:
    """菜单一致性分析器"""

    def __init__(self):
        self.issues = []

    def analyze_all_menus(self) -> Dict[str, Any]:
        """分析所有菜单的一致性问题"""
        try:
            menus = DynamicMenu.objects.all()
            issues = []

            for menu in menus:
                menu_issues = self._analyze_menu_consistency(menu)
                issues.extend(menu_issues)

            # 按严重程度分类
            severity_breakdown = {
                'high': len([i for i in issues if i['severity'] == 'high']),
                'medium': len([i for i in issues if i['severity'] == 'medium']),
                'low': len([i for i in issues if i['severity'] == 'low'])
            }

            return {
                'total_menus': len(menus),
                'total_issues': len(issues),
                'issues': issues,
                'severity_breakdown': severity_breakdown
            }

        except Exception as e:
            logger.error(f"一致性分析失败: {str(e)}", exc_info=True)
            return {
                'total_menus': 0,
                'total_issues': 0,
                'issues': [],
                'severity_breakdown': {'high': 0, 'medium': 0, 'low': 0}
            }

    def _analyze_menu_consistency(self, menu: DynamicMenu) -> List[Dict[str, Any]]:
        """分析单个菜单的一致性"""
        issues = []

        try:
            # 检查路径与名称一致性
            expected_path = self._generate_expected_path(menu.name, menu.parent_id)
            if menu.path != expected_path:
                issues.append({
                    'type': 'path_mismatch',
                    'menu_id': menu.id,
                    'menu_name': menu.name,
                    'description': f'路径不匹配：当前 "{menu.path}"，期望 "{expected_path}"',
                    'severity': 'medium',
                    'current_value': menu.path,
                    'expected_value': expected_path
                })

            # 检查组件名一致性
            if menu.component_name:
                expected_component = self._generate_expected_component_name(menu.path)
                if menu.component_name != expected_component:
                    issues.append({
                        'type': 'component_mismatch',
                        'menu_id': menu.id,
                        'menu_name': menu.name,
                        'description': f'组件名不匹配：当前 "{menu.component_name}"，期望 "{expected_component}"',
                        'severity': 'low',
                        'current_value': menu.component_name,
                        'expected_value': expected_component
                    })

            # 检查文件是否存在
            if menu.component_name and menu.is_generated:
                file_path = self._get_component_file_path(menu.component_name)
                if not os.path.exists(file_path):
                    issues.append({
                        'type': 'file_missing',
                        'menu_id': menu.id,
                        'menu_name': menu.name,
                        'description': f'组件文件不存在：{file_path}',
                        'severity': 'high',
                        'missing_file': file_path
                    })

            # 检查图标格式
            if menu.icon and not menu.icon.startswith('fa-'):
                issues.append({
                    'type': 'icon_format',
                    'menu_id': menu.id,
                    'menu_name': menu.name,
                    'description': f'图标格式不正确：{menu.icon}',
                    'severity': 'low',
                    'current_value': menu.icon
                })

        except Exception as e:
            logger.error(f"分析菜单 {menu.id} 失败: {str(e)}")
            issues.append({
                'type': 'analysis_error',
                'menu_id': menu.id,
                'menu_name': menu.name,
                'description': f'分析过程出错：{str(e)}',
                'severity': 'medium'
            })

        return issues

    def _generate_expected_path(self, name: str, parent_id: Optional[int]) -> str:
        """生成期望的路径"""
        # 简化的路径生成逻辑
        path_name = self._chinese_to_english(name)

        if parent_id:
            try:
                parent = DynamicMenu.objects.get(id=parent_id)
                return f"{parent.path}/{path_name}"
            except DynamicMenu.DoesNotExist:
                return f"/{path_name}"
        else:
            return f"/{path_name}"

    def _generate_expected_component_name(self, path: str) -> str:
        """生成期望的组件名"""
        # 移除开头的斜杠并转换为驼峰命名
        clean_path = path.lstrip('/')
        parts = clean_path.split('/')
        return ''.join(word.capitalize() for word in parts if word)

    def _chinese_to_english(self, chinese_text: str) -> str:
        """中文转英文路径"""
        translation_map = {
            '管理': 'management',
            '系统': 'system',
            '用户': 'user',
            '主机': 'host',
            '配置': 'config',
            '监控': 'monitor',
            '日志': 'log',
            '审计': 'audit',
            '权限': 'permission',
            '角色': 'role',
            '菜单': 'menu',
            '设置': 'settings',
            '数据': 'data',
            '文件': 'file',
            '网络': 'network',
            '安全': 'security',
            '备份': 'backup',
            '报表': 'report',
            '任务': 'task',
            '项目': 'project',
            # 新增更多映射
            'Ansible': 'ansible',
            'SSH': 'ssh',
            'CMDB': 'cmdb',
            '主机管理': 'host-management',
            '连接管理': 'connection-management',
            '会话管理': 'session-management',
            '回放': 'replay',
            '终端': 'terminal'
        }

        result = chinese_text
        for chinese, english in translation_map.items():
            result = result.replace(chinese, english)

        # 清理特殊字符
        result = re.sub(r'[^\w\u4e00-\u9fff]', '-', result)
        result = re.sub(r'[\u4e00-\u9fff]', '', result)
        result = re.sub(r'-+', '-', result)
        result = result.strip('-').lower()

        return result if result else 'custom-menu'

    def _get_component_file_path(self, component_name: str) -> str:
        """获取组件文件路径"""
        # 使用settings中配置的前端路径
        frontend_path = getattr(settings, 'FRONTEND_PATH', settings.BASE_DIR.parent / 'devops-frontend')
        return os.path.join(str(frontend_path), 'src', 'views', f'{component_name}.vue')

    def fix_all_issues(self, options: Dict[str, Any]) -> Dict[str, Any]:
        """修复所有一致性问题"""
        try:
            analysis = self.analyze_all_menus()
            issues = analysis['issues']

            fixed_count = 0
            failed_count = 0
            results = []

            with transaction.atomic():
                for issue in issues:
                    try:
                        result = self._fix_single_issue(issue, options)
                        if result['success']:
                            fixed_count += 1
                        else:
                            failed_count += 1
                        results.append(result)
                    except Exception as e:
                        failed_count += 1
                        results.append({
                            'success': False,
                            'issue': issue,
                            'error': str(e)
                        })

            return {
                'fixed_count': fixed_count,
                'failed_count': failed_count,
                'results': results
            }

        except Exception as e:
            logger.error(f"批量修复失败: {str(e)}", exc_info=True)
            raise e

    def _fix_single_issue(self, issue: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
        """修复单个问题"""
        try:
            menu = DynamicMenu.objects.get(id=issue['menu_id'])

            if issue['type'] == 'path_mismatch':
                if options.get('update_path', True):
                    menu.path = issue['expected_value']
                    menu.save()
                    return {'success': True, 'action': 'path_updated'}

            elif issue['type'] == 'component_mismatch':
                if options.get('update_component', True):
                    menu.component_name = issue['expected_value']
                    menu.save()
                    return {'success': True, 'action': 'component_updated'}

            elif issue['type'] == 'file_missing':
                if options.get('create_missing_files', True):
                    self._create_missing_component_file(issue['missing_file'])
                    return {'success': True, 'action': 'file_created'}

            elif issue['type'] == 'icon_format':
                if options.get('fix_icon_format', True):
                    # 简单的图标格式修复
                    if not menu.icon.startswith('fa-'):
                        menu.icon = f"fa-{menu.icon}"
                        menu.save()
                    return {'success': True, 'action': 'icon_fixed'}

            return {'success': False, 'reason': 'no_action_taken'}

        except Exception as e:
            logger.error(f"修复问题失败: {str(e)}")
            return {'success': False, 'error': str(e)}

    def _create_missing_component_file(self, file_path: str):
        """创建缺失的组件文件"""
        try:
            os.makedirs(os.path.dirname(file_path), exist_ok=True)

            # 创建基础的Vue组件模板
            template = '''<template>
  <div class="ansible-config-container">
    <!-- 星空背景 -->
    <div class="starry-background">
      <div class="stars"></div>
      <div class="stars2"></div>
      <div class="stars3"></div>
    </div>

    <!-- 主内容区域 -->
    <div class="main-content">
      <!-- 页面标题 -->
      <div class="page-header">
        <h1 class="page-title">
          <i class="fa fa-cog"></i>
          页面标题
        </h1>
        <div class="page-description">
          页面描述
        </div>
      </div>

      <!-- 内容区域 -->
      <div class="glass-panel">
        <div class="panel-header">
          <h3><i class="fa fa-list"></i> 内容</h3>
        </div>
        <div class="panel-content">
          <p style="color: #fff;">页面内容</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
// 页面逻辑
</script>

<style scoped>
/* 导入ansible基础样式 */
@import '@/styles/templates/ansible-base.scss';

/* 确保包含所有必需的样式 */
.ansible-config-container {
  position: relative;
  min-height: 100vh;
  background: linear-gradient(135deg,
    rgba(0, 0, 0, 0.9) 0%,
    rgba(20, 20, 40, 0.95) 50%,
    rgba(0, 0, 0, 0.9) 100%
  );
  overflow-x: hidden;
}

.glass-panel {
  background: rgba(164, 244, 186, 0.05);
  border: 1px solid rgba(164, 244, 186, 0.2);
  border-radius: 15px;
  backdrop-filter: blur(20px);
  box-shadow:
    0 8px 32px rgba(0, 0, 0, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
  margin-bottom: 20px;
  overflow: hidden;
}
</style>'''

            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(template)

            logger.info(f"创建组件文件: {file_path}")

        except Exception as e:
            logger.error(f"创建组件文件失败: {str(e)}")
            raise e


class MenuOperationService:
    """菜单操作服务"""

    def create_menu(self, data: Dict[str, Any], user) -> Dict[str, Any]:
        """创建菜单"""
        try:
            # 生成路径和组件名
            path_generator = SmartPathGenerator()
            path_info = path_generator.generate_menu_path(data)

            # 创建菜单对象
            menu = DynamicMenu.objects.create(
                name=data['name'],
                path=path_info['routePath'],
                icon=data['icon'],
                parent_id=data.get('parent_id'),
                component_name=path_info.get('componentName', ''),
                is_generated=data.get('generate_page', False),
                description=data.get('description', ''),
                permission_code=','.join(data.get('permissions', []))
            )

            # 如果需要生成页面文件
            if data.get('generate_page', False):
                self._create_page_file(menu, path_info)

            return {
                'id': menu.id,
                'name': menu.name,
                'path': menu.path,
                'icon': menu.icon,
                'component_name': menu.component_name,
                'is_generated': menu.is_generated
            }

        except Exception as e:
            logger.error(f"创建菜单失败: {str(e)}", exc_info=True)
            raise e

    def update_menu(self, menu_id: int, data: Dict[str, Any], user) -> Dict[str, Any]:
        """更新菜单"""
        try:
            menu = DynamicMenu.objects.get(id=menu_id)

            # 更新字段
            if 'name' in data:
                menu.name = data['name']
            if 'icon' in data:
                menu.icon = data['icon']
            if 'permissions' in data:
                menu.permission_code = ','.join(data['permissions'])

            menu.save()

            return {
                'id': menu.id,
                'name': menu.name,
                'path': menu.path,
                'icon': menu.icon,
                'component_name': menu.component_name
            }

        except DynamicMenu.DoesNotExist:
            raise ValidationError('菜单不存在')
        except Exception as e:
            logger.error(f"更新菜单失败: {str(e)}", exc_info=True)
            raise e

    def delete_menu(self, menu_id: int, options: Dict[str, Any], user) -> Dict[str, Any]:
        """删除菜单"""
        try:
            menu = DynamicMenu.objects.get(id=menu_id)

            deleted_count = 1

            # 处理子菜单
            children_strategy = options.get('children_strategy', 'cascade')
            if children_strategy == 'cascade':
                # 级联删除
                children = DynamicMenu.objects.filter(parent_id=menu_id)
                deleted_count += children.count()
                children.delete()

            # 删除文件
            if options.get('delete_files', False) and menu.component_name:
                self._delete_component_file(menu.component_name)

            # 删除菜单
            menu.delete()

            return {
                'deleted_count': deleted_count
            }

        except DynamicMenu.DoesNotExist:
            raise ValidationError('菜单不存在')
        except Exception as e:
            logger.error(f"删除菜单失败: {str(e)}", exc_info=True)
            raise e

    def analyze_deletion_impact(self, menu_id: int) -> Dict[str, Any]:
        """分析删除影响"""
        try:
            menu = DynamicMenu.objects.get(id=menu_id)

            # 获取子菜单
            children = list(DynamicMenu.objects.filter(parent_id=menu_id).values(
                'id', 'name', 'path'
            ))

            # 分析文件影响
            file_operations = []
            if menu.component_name:
                file_operations.append({
                    'type': 'delete_component',
                    'file': menu.component_name,
                    'path': self._get_component_file_path(menu.component_name)
                })

            # 风险等级评估
            risk_level = 'low'
            if len(children) > 0:
                risk_level = 'high'
            elif menu.component_name:
                risk_level = 'medium'

            return {
                'menu': {
                    'id': menu.id,
                    'name': menu.name,
                    'path': menu.path
                },
                'children': children,
                'affected_users': [],  # 这里可以扩展用户影响分析
                'file_operations': file_operations,
                'risk_level': risk_level
            }

        except DynamicMenu.DoesNotExist:
            raise ValidationError('菜单不存在')
        except Exception as e:
            logger.error(f"分析删除影响失败: {str(e)}", exc_info=True)
            raise e

    def handle_drag_drop(self, dragged_id: int, target_id: int, drop_type: str, user) -> Dict[str, Any]:
        """处理拖拽操作"""
        try:
            dragged_menu = DynamicMenu.objects.get(id=dragged_id)
            target_menu = DynamicMenu.objects.get(id=target_id)

            # 简单的拖拽逻辑实现
            if drop_type == 'inner':
                # 移动到目标菜单内部
                dragged_menu.parent_id = target_id
                dragged_menu.save()
            elif drop_type == 'before' or drop_type == 'after':
                # 移动到目标菜单同级
                dragged_menu.parent_id = target_menu.parent_id
                dragged_menu.save()

            return {
                'success': True,
                'needs_refresh': False
            }

        except DynamicMenu.DoesNotExist:
            raise ValidationError('菜单不存在')
        except Exception as e:
            logger.error(f"拖拽操作失败: {str(e)}", exc_info=True)
            raise e

    def _create_page_file(self, menu: DynamicMenu, path_info: Dict[str, Any]):
        """创建页面文件"""
        if path_info.get('filePath'):
            analyzer = MenuConsistencyAnalyzer()
            analyzer._create_missing_component_file(path_info['filePath'])

    def _delete_component_file(self, component_name: str):
        """删除组件文件"""
        try:
            file_path = self._get_component_file_path(component_name)
            if os.path.exists(file_path):
                os.remove(file_path)
                logger.info(f"删除组件文件: {file_path}")
        except Exception as e:
            logger.error(f"删除组件文件失败: {str(e)}")

    def _get_component_file_path(self, component_name: str) -> str:
        """获取组件文件路径"""
        frontend_path = getattr(settings, 'FRONTEND_PATH', settings.BASE_DIR.parent / 'devops-frontend')
        return os.path.join(str(frontend_path), 'src', 'views', f'{component_name}.vue')


class SmartPathGenerator:
    """智能路径生成器"""

    def generate_menu_path(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """生成菜单路径信息"""
        try:
            analyzer = MenuConsistencyAnalyzer()

            # 生成基础路径
            base_path = analyzer._generate_expected_path(data['name'], data.get('parent_id'))

            # 生成组件名
            component_name = analyzer._generate_expected_component_name(base_path)

            # 生成文件路径
            file_path = analyzer._get_component_file_path(component_name)

            # 检测冲突
            conflicts = self._detect_conflicts(base_path, component_name)

            # 生成建议
            suggestions = self._generate_suggestions(base_path) if conflicts else []

            return {
                'routePath': base_path,
                'componentName': component_name,
                'filePath': file_path,
                'conflicts': conflicts,
                'suggestions': suggestions
            }

        except Exception as e:
            logger.error(f"路径生成失败: {str(e)}", exc_info=True)
            raise e

    def _detect_conflicts(self, path: str, component_name: str) -> List[str]:
        """检测路径冲突"""
        conflicts = []

        # 检查路径冲突
        if DynamicMenu.objects.filter(path=path).exists():
            conflicts.append(f'路径冲突: {path} 已存在')

        # 检查组件名冲突
        if DynamicMenu.objects.filter(component_name=component_name).exists():
            conflicts.append(f'组件名冲突: {component_name} 已存在')

        return conflicts

    def _generate_suggestions(self, base_path: str) -> List[str]:
        """生成路径建议"""
        suggestions = []

        for i in range(1, 4):
            suggestion = f"{base_path}-{i}"
            if not DynamicMenu.objects.filter(path=suggestion).exists():
                suggestions.append(suggestion)

        return suggestions


class MenuBackupService:
    """菜单备份服务"""

    def create_backup(self, backup_type: str, description: str, created_by) -> str:
        """创建备份"""
        try:
            backup_id = f"backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{created_by.id}"

            # 收集备份数据
            backup_data = {
                'id': backup_id,
                'type': backup_type,
                'description': description,
                'created_by': created_by.username,
                'created_at': datetime.now().isoformat(),
                'menus': list(DynamicMenu.objects.all().values()),
                'version': '1.0.0'
            }

            # 保存备份文件
            backup_dir = getattr(settings, 'BACKUP_DIR', '/tmp/menu_backups')
            os.makedirs(backup_dir, exist_ok=True)

            backup_file = os.path.join(backup_dir, f"{backup_id}.json")
            with open(backup_file, 'w', encoding='utf-8') as f:
                json.dump(backup_data, f, ensure_ascii=False, indent=2)

            logger.info(f"创建备份: {backup_id}")
            return backup_id

        except Exception as e:
            logger.error(f"创建备份失败: {str(e)}", exc_info=True)
            raise e

    def restore_backup(self, backup_id: str, user) -> Dict[str, Any]:
        """恢复备份"""
        try:
            backup_dir = getattr(settings, 'BACKUP_DIR', '/tmp/menu_backups')
            backup_file = os.path.join(backup_dir, f"{backup_id}.json")

            if not os.path.exists(backup_file):
                raise ValidationError('备份文件不存在')

            with open(backup_file, 'r', encoding='utf-8') as f:
                backup_data = json.load(f)

            # 恢复菜单数据
            with transaction.atomic():
                # 清空现有菜单
                DynamicMenu.objects.all().delete()

                # 恢复菜单
                for menu_data in backup_data['menus']:
                    menu_data.pop('id', None)  # 移除ID，让数据库自动生成
                    DynamicMenu.objects.create(**menu_data)

            logger.info(f"恢复备份: {backup_id}")
            return {
                'restored_menus': len(backup_data['menus']),
                'backup_date': backup_data.get('created_at')
            }

        except Exception as e:
            logger.error(f"恢复备份失败: {str(e)}", exc_info=True)
            raise e

    def get_backup_count(self) -> int:
        """获取备份数量"""
        try:
            backup_dir = getattr(settings, 'BACKUP_DIR', '/tmp/menu_backups')
            if not os.path.exists(backup_dir):
                return 0

            backup_files = [f for f in os.listdir(backup_dir) if f.endswith('.json')]
            return len(backup_files)

        except Exception as e:
            logger.error(f"获取备份数量失败: {str(e)}")
            return 0

    def get_last_backup_time(self) -> Optional[str]:
        """获取最后备份时间"""
        try:
            backup_dir = getattr(settings, 'BACKUP_DIR', '/tmp/menu_backups')
            if not os.path.exists(backup_dir):
                return None

            backup_files = [f for f in os.listdir(backup_dir) if f.endswith('.json')]
            if not backup_files:
                return None

            # 获取最新的备份文件
            latest_file = max(backup_files, key=lambda f: os.path.getctime(
                os.path.join(backup_dir, f)
            ))

            file_path = os.path.join(backup_dir, latest_file)
            with open(file_path, 'r', encoding='utf-8') as f:
                backup_data = json.load(f)

            return backup_data.get('created_at')

        except Exception as e:
            logger.error(f"获取最后备份时间失败: {str(e)}")
            return None
